/**
 * User: Obi
 * Date: 31.07.11
 * Time: 14:38
 */
package com.jediflasher.game_client.server {
	import com.jediflasher.game_client.events.server.ServerConnectorEvent;
	import com.jediflasher.game_client.server.messages.Header;
	import com.jediflasher.game_client.utils.logging.Logger;
	import com.jediflasher.game_client.utils.logging.LoggerEvent;
	import com.jediflasher.game_client.utils.logging.LoggerMessageLevel;
	import com.netease.protobuf.Message;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.system.Security;
	import flash.system.System;
	import flash.utils.ByteArray;

	[Event(name="connect", type="com.jediflasher.game_client.events.server.ServerConnectorEvent")]
	[Event(name="error", type="com.jediflasher.game_client.events.server.ServerConnectorEvent")]
	[Event(name="disconnect", type="com.jediflasher.game_client.events.server.ServerConnectorEvent")]

	[Event(name="messageReceived", type="com.jediflasher.game_client.events.server.ServerConnectorEvent")]

	public class ServerConnector extends EventDispatcher {

		private static var _requestCounter:int = 1;

		public function ServerConnector(host:String, port:int, policyPort:int) {

			var pAddr:String = "http://"+host+":"+policyPort;
//			var pAddr:String = "xmlsocket://"+host+":"+policyPort;
			Security.allowDomain(host);
			Logger.log("Allow domain "+host);
			Security.loadPolicyFile(pAddr);
 			Logger.log("Loading policy file from "+pAddr);

			this._host = host;
			this._port = port;

			this.initSocket();
		}

		/**
		 * @private
		 */
		private var _socket:Socket;

		/**
		 * @private
		 */
		private var _host:String;

		/**
		 * @private
		 */
		private var _port:int;

		/**
		 * @private
		 */
		private var _currentMessageLength:int = -1;


		public function send(messageType:int, serviceType:int, methodType:int, message:Message, requestId:int):void {

			var messageByteArray:ByteArray = new ByteArray();
			message.writeTo(messageByteArray);

			var header:Header = new Header();
			header.messageType = messageType;
			header.serviceType = serviceType;
			header.method = methodType;
			header.requestId = requestId;
			header.bodySize = messageByteArray.length;

			messageByteArray = new ByteArray();

			header.writeTo(messageByteArray);
			var headerSize:int = messageByteArray.length;

			message.writeTo(messageByteArray);

			var bytesToSend:ByteArray = new ByteArray();
			bytesToSend.writeInt(messageByteArray.length + 4); // добавляем 4 байта – длина заголовка
			bytesToSend.writeInt(headerSize);
			bytesToSend.writeBytes(messageByteArray);

			Logger.log("======== output =======");
			Logger.log("header >\n" + header);
			Logger.log("message >\n" + message.toString());
			Logger.log("totalBytes: " + bytesToSend.length + "\n");

			this._socket.writeBytes(bytesToSend);
			this._socket.flush();
		}

		/**
		 * Возвращает уникальный идентификатор, по которому можно отследить запрос
		 * @return уникальный идентификатор запроса
		 */
		public static function getRequestId():int {
			return _requestCounter++;
		}

		/**
		 * @private
		 */
		private function initSocket():void {
			this._socket = new Socket();
			this._socket.addEventListener(Event.CONNECT, this.handler_connect);
			this._socket.addEventListener(IOErrorEvent.IO_ERROR, this.handler_ioError);
			this._socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.handler_securityError);
			this._socket.addEventListener(Event.CLOSE, this.handler_close);
			this._socket.addEventListener(ProgressEvent.SOCKET_DATA, this.handler_socketData);
			this._socket.connect(this._host, this._port);

			trace("Socket init with host:", this._host + ":" + this._port);
		}

		/**
		 * @private
		 */
		private function parseMessage():void {
			var totalBytes:int = this._socket.bytesAvailable;
			var messageWrapper:MessageWrapper = new MessageWrapper().read(this._socket);
			Logger.log("======== input =======");
			Logger.log(messageWrapper.toString());
			Logger.log("totalBytes: " + totalBytes + "\n");
			this.parsingComplete(messageWrapper);
		}

		private function parsingComplete(messageWrapper:MessageWrapper):void {
			super.dispatchEvent(new ServerConnectorEvent(ServerConnectorEvent.MESSAGE_RECEIVED, messageWrapper));
		}

		/**
		 * @private
		 */
		private function handler_connect(event:Event):void {
			Logger.log("Connected to server");
			super.dispatchEvent(new ServerConnectorEvent(ServerConnectorEvent.CONNECT));
		}

		/**
		 * @private
		 */
		private function handler_securityError(event:SecurityErrorEvent):void {
			Logger.log("securityError: "+ event.text, LoggerMessageLevel.ERROR);
			super.dispatchEvent(new ServerConnectorEvent(ServerConnectorEvent.ERROR));
		}

		/**
		 * @private
		 */
		private function handler_ioError(event:IOErrorEvent):void {
			Logger.log("i/o error: "+ event.text, LoggerMessageLevel.ERROR);
			super.dispatchEvent(new ServerConnectorEvent(ServerConnectorEvent.ERROR));
		}

		/**
		 * @private
		 */
		private function handler_close(event:Event):void {
			Logger.log("Connection closed");
			super.dispatchEvent(new ServerConnectorEvent(ServerConnectorEvent.DISCONNECT));
		}

		/**
		 * @private
		 */
		private function handler_socketData(event:ProgressEvent):void {
			
			while(this._socket.bytesAvailable > 0 && this._socket.bytesAvailable >= this._currentMessageLength) {
				// новое сообщение
				if (this._currentMessageLength == -1) {
					this._currentMessageLength = this._socket.readInt();
				}

				if (this._socket.bytesAvailable >= this._currentMessageLength) {
					this.parseMessage();
					this._currentMessageLength = -1;
				}
			}
		}

	}
}
